﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MindFusion.Diagramming.WinForms;
using CommonStructs;
using MindFusion.Diagramming;

namespace FrameNetViewingControl
{
	public partial class FrameNetDiagram : UserControl
	{
		DiagramView diagramView;

		ViewState viewState = ViewState.Custom;
		LinkType linkType = LinkType.IsA;

		/// <summary>
		/// Поведение диаграммы
		/// </summary>
		public ViewState DiagramViewState
		{
			get
			{
				return viewState;
			}
			set
			{
				switch (value)
				{
					case (ViewState.DoNothing):
						diagramView.Behavior = Behavior.DoNothing;
						break;
					case (ViewState.Custom):
						diagramView.Behavior = Behavior.Custom;
						break;
					case (ViewState.Modify):
						diagramView.Behavior = Behavior.Modify;
						break;
					case (ViewState.DrawLinks):
						diagramView.Behavior = Behavior.DrawLinks;
						break;
					case (ViewState.Pan):
						diagramView.Behavior = Behavior.Pan;
						break;
					default:
						break;
				}
				viewState = value;
			}
		}

		/// <summary>
		/// Тип дуги, которая будет рисоваться
		/// </summary>
		public LinkType LinkType
		{
			get { return linkType; }
			set { linkType = value; }
		}

		public FrameNetDiagram()
		{
			InitializeComponent();
			diagramView = new DiagramView(diagram);
		}

		#region ToolStrip
		private void toolStripMenuItemCreateFrame_Click(object sender, EventArgs e)
		{
			EventFrameCreate(new Point(Convert.ToInt32(CurMousePos.X), Convert.ToInt32(CurMousePos.Y)));
		}

		private void триToolStripMenuItemNodeRemove_Click(object sender, EventArgs e)
		{
			if (MessageBox.Show("Удалить фрейм?", "Подтверждение удаления", MessageBoxButtons.YesNo) == DialogResult.Yes)
			{
				DeleteFrame(Convert.ToInt32((sender as ToolStripDropDownItem).Tag.ToString()));
			}
		}

		private void переименоватьToolStripMenuItemNodeRename_Click(object sender, EventArgs e)
		{
			ShapeNode shapeNode = GetNodeByID(Convert.ToInt32((sender as ToolStripMenuItem).Tag.ToString()));
			diagramView.BeginEdit(shapeNode);
		}	

		private void toolStripMenuItemLinkRemove_Click(object sender, EventArgs e)
		{
			if (MessageBox.Show("Удалить связь?", "Подтверждение удаления", MessageBoxButtons.YesNo) == DialogResult.Yes)
			{
				Point p = (Point)(sender as ToolStripMenuItem).Tag;
				DiagramLink link = GetLinkByStartPos(p);
				EventLinkRemove(Convert.ToInt32(link.Origin.Id), Convert.ToInt32(link.Destination.Id),
								link.Tag.ToString() == CommonStructs.LinkType.IsA.ToString()
									? CommonStructs.LinkType.IsA
									: CommonStructs.LinkType.SubFrame);
			}
		}
		#endregion		

		private PointF CurMousePos;

		private void diagram_Clicked(object sender, DiagramEventArgs e)
		{
			CurMousePos = e.MousePosition;
			if (e.MouseButton == MouseButton.Left && (DiagramViewState == ViewState.DoNothing))
			{
				EventFrameCreate(new Point(Convert.ToInt32(e.MousePosition.X), Convert.ToInt32(e.MousePosition.Y)));
			}
			if (e.MouseButton == MouseButton.Right)
			{
				contextMenuStripView.Items[0].Visible = true;
				contextMenuStripView.Items[1].Visible = false;
				contextMenuStripView.Items[2].Visible = false;
				contextMenuStripView.Items[3].Visible = false;
			}
		}

		#region Public Interface
		public event Action<int, int, LinkType> EventLinkCreate;
		public event Action<int, int, LinkType> EventLinkRemove;		

		public event Action<Point> EventFrameCreate;
		public event Action<int> EventFrameRemove;
		public event Action<int> EventFrameSelect;
		public event Action<int, Point> EventFrameMove;
		public event Action<int, string> EventFrameRename;

		public event System.Action EventUnselectAll;

		public void PreviewKeyPressed(Keys key)
		{
			if (key == Keys.Left || key == Keys.Right || key == Keys.Up || key == Keys.Down)
				foreach (ShapeNode c in diagram.Selection.Nodes)
				{
					switch (key)
					{
						case (Keys.Left):
							EventFrameMove(Convert.ToInt32(c.Id), new Point(-5, 0));
							break;
						case (Keys.Right):
							EventFrameMove(Convert.ToInt32(c.Id), new Point(5, 0));
							break;
						case (Keys.Up):
							EventFrameMove(Convert.ToInt32(c.Id), new Point(0, -5));
							break;
						case (Keys.Down):
							EventFrameMove(Convert.ToInt32(c.Id), new Point(0, 5));
							break;
						default:
							break;
					}
				}
		}

		/// <summary>
		/// Добавить вершину
		/// </summary>
		/// <param name="fdata">параметры добавляемой вершины</param>
		public void AddFrame(FrameData fdata)
		{
			var shapeNode = new ShapeNode
			{
				Bounds = new RectangleF(fdata.location, new SizeF(60, 30)),
				Id = fdata.id,
				Text = fdata.name,
				TextPadding = new Thickness(2),
				Expandable = true,
				Tag = true,
			};
			diagram.Nodes.Add(shapeNode);
			UnSelectFrame(int.Parse(shapeNode.Id.ToString()));
			shapeNode.ResizeToFitText(FitSize.KeepRatio);
			// TODO: добавить контекстное меню
		}

		/// <summary>
		/// Удалить вершину
		/// </summary>
		/// <param name="id"> уникальный идентификатор удаляемой вершины</param>
		public void DeleteFrame(int id)
		{
			ShapeNode shapeNode = GetNodeByID(id);
			//foreach (var variable in
			//    diagram.Nodes.Cast<object>().Where(c => (int) ((ShapeNode) c).Id == id))
			//{
			//    shapeNode = (ShapeNode)variable;
			//    break;
			//}
			diagram.NodeDeleted -= diagram_NodeDeleted;
			diagram.Nodes.Remove(shapeNode);
			diagram.NodeDeleted += diagram_NodeDeleted;
		}

		/// <summary>
		/// Выделить вершину
		/// </summary>
		/// <param name="id">уникальный идентификатор выделяемой вершины</param>
		public void SelectFrame(int id)
		{
			var shapeNode = GetNodeByID(id);

			shapeNode.TextFormat.Alignment = StringAlignment.Center;
			shapeNode.Brush = new MindFusion.Drawing.LinearGradientBrush(Color.DeepPink, Color.DeepSkyBlue, 60);
			shapeNode.Shape = Shapes.RoundRect;
			shapeNode.Font = new Font("Consolas", 14, GraphicsUnit.World);
			shapeNode.TextColor = Color.White;
			foreach (var VARIABLE in shapeNode.OutgoingLinks)
			{
				DiagramLink link = (DiagramLink)VARIABLE;
				LinkChangeStyle(link, link.Tag.ToString() == LinkType.IsA.ToString() ? Color.LightGray : Color.DarkBlue, 5);
			}
			//foreach (var VARIABLE in shapeNode.IncomingLinks)
			//{
			//    DiagramLink link = (DiagramLink)VARIABLE;
			//    LinkChangeStyle(link, Color.DeepPink, 6);
			//}
			//shapeNode.Selected = true;
		}

		/// <summary>
		/// Снять выделение вершины
		/// </summary>
		/// <param name="id">уникальный идентификатор вершины</param>
		public void UnSelectFrame(int id)
		{
			var shapeNode = GetNodeByID(id);
			shapeNode.TextFormat.Alignment = StringAlignment.Center;
			shapeNode.Brush = new  MindFusion.Drawing.SolidBrush("#FFFFE4C4");
			shapeNode.Shape = Shapes.RoundRect;
			shapeNode.Font = new Font("Consolas", 14, GraphicsUnit.World);
			shapeNode.TextColor = Color.Black;
			foreach (var VARIABLE in shapeNode.OutgoingLinks)
			{
				var link = (DiagramLink)VARIABLE;
				LinkChangeStyle(link, link.Tag.ToString() == LinkType.IsA.ToString() ? ColorPreferences.LinkIsAColor : ColorPreferences.LinkSubFrameColor, 3);
			}
			//foreach (var VARIABLE in shapeNode.IncomingLinks)
			//{
			//    DiagramLink link = (DiagramLink)VARIABLE;
			//    LinkChangeStyle(link, Color.DodgerBlue, 3);
			//}
			// shapeNode.Selected = false;
		}

		public void FrameRename(int id, String newText)
		{
			var shapeNode = GetNodeByID(id);
			shapeNode.Text = newText;
			shapeNode.ResizeToFitText(FitSize.KeepRatio);
		}

		public void LinkAdd(int from, int to, LinkType type)
		{
			var link = new DiagramLink(diagram, GetNodeByID(from), GetNodeByID(to))
			{
				Pen = { Width = 3 },
				AutoRoute = false,
				Dynamic = true,
				//    HeadShape = Shapes.Arrow9,
				HeadShapeSize = 20,
				TextStyle = LinkTextStyle.Rotate,
				Style = LinkStyle.Polyline,
				SegmentCount = 1,
				Tag = type.ToString(),
				Text = type.ToString()
			};
			//switch (type)
			//{
			//    case LinkType.IsA:
			//        link.Text = "Is_a";
			//        break;
			//    case LinkType.Subframe:
			//        link.Text = "Has";
			//        break;
			//    default:
			//        break;
			//}
			LinkChangeStyle(link, link.Tag.ToString() == LinkType.IsA.ToString() ? ColorPreferences.LinkIsAColor : ColorPreferences.LinkSubFrameColor, 3);
			link.TextAlignment = StringAlignment.Center;
			diagram.Links.Add(link);
		}

		public void LinkRemove(int from, int to)
		{
			var link = GetLinkByEnds(from, to);
			diagram.Links.Remove(link);
		}

		public void UnselectAll()
		{
			foreach (var node in diagram.Nodes.Cast<object>().Where(node => ((ShapeNode)node).Selected))
			{
				UnSelectFrame(int.Parse(((ShapeNode)node).Id.ToString()));
			}
		}		

		public void DelPress()
		{

		}

		public void Clear()
		{
			diagram.ClearAll();
		}
		#endregion
		//public delegate void DelegateEmpty();
		//public delegate void DelegatePoint(Point point);
		//public delegate void DelegateInt(int i);
		//public delegate void DelegateIntInt(int i, int j);
		//public delegate void DelegateIntIntType(int i, int j, LinkType type);
		//public delegate void DelegateIntPoint(int i, Point point);
		//public delegate void DelegateIntString(int frameId, string newName);		

		#region Node's Events

		private void diagram_NodeSelected(object sender, MindFusion.Diagramming.NodeEventArgs e)
		{
			var sNode = ((ShapeNode)e.Node);
			SelectFrame(int.Parse(sNode.Id.ToString()));
			EventFrameSelect(int.Parse(sNode.Id.ToString()));
		}

		private void diagram_NodeDeselected(object sender, NodeEventArgs e)
		{
			var sNode = ((ShapeNode)e.Node);
			UnSelectFrame(int.Parse(sNode.Id.ToString()));
			//if (diagram.Selection.Nodes.Count == 1)
			//    EventUnselectAll();
		}

		private void DiagramSelectionChanged(object sender, System.EventArgs e)
		{
			//ShapeNode shapeNode;
			if (diagram.Selection.Nodes.Count == 0)
				EventUnselectAll();
			//else
			//foreach (var item in diagram.Nodes)
			//{
			//    shapeNode = (ShapeNode) item;
			//    if (shapeNode.Selected)
			//    {
			//        SelectFrame(int.Parse(shapeNode.Id.ToString()));
			//    }
			//    else
			//    {
			//        UnSelectFrame(int.Parse(shapeNode.Id.ToString()));
			//    }
			//}
		}

		private void DiagramNodeModified(object sender, NodeEventArgs e)
		{
			var shapeNode = e.Node;
			EventFrameMove(int.Parse(shapeNode.Id.ToString()), new Point((int)shapeNode.Bounds.Left, (int)shapeNode.Bounds.Top));
		}

		private void DiagramNodeTextEdited(object sender, EditNodeTextEventArgs e)
		{
			var shapeNode = (ShapeNode)e.Node;
			EventFrameRename(int.Parse(shapeNode.Id.ToString()), shapeNode.Text);
		}

		private void DiagramViewPreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
		{
			switch (e.KeyData)
			{
				case (Keys.Enter):
					break;
				case (Keys.Delete):
					var nodes = diagram.Nodes.Cast<object>().Where(variable => ((ShapeNode)variable).Selected).Cast<DiagramNode>().ToList();
					foreach (var node in nodes)
					{
						if (MessageBox.Show("Удалить фрейм?", "Подтверждение удаления", MessageBoxButtons.YesNo) == DialogResult.Yes)
						{
							EventFrameRemove(Int32.Parse(node.Id.ToString()));
							//                        diagram.Nodes.Remove(node);
						}
					}
					break;
				case (Keys.A):
					//if (e.Control)
					//{                    
					foreach (var node in diagram.Nodes)
					{
						((ShapeNode)node).Selected = true;
					}
					//  }
					break;
			}
			if (e.KeyData == Keys.Enter)
			{

			}
		}

		private void DiagramNodeDeleting(object sender, MindFusion.Diagramming.NodeValidationEventArgs e)
		{
			e.Cancel = true;
		}

		private void diagram_NodeDeleted(object sender, MindFusion.Diagramming.NodeEventArgs e)
		{
			diagram.Nodes.Add(e.Node);
			//EventFrameRemove(Int32.Parse(e.Node.Id.ToString()));
			//e.Cancel = true;
		}

		private void diagram_NodeDoubleClicked(object sender, MindFusion.Diagramming.NodeEventArgs e)
		{
			if (DiagramViewState == CommonStructs.ViewState.Modify)
				diagramView.BeginEdit((ShapeNode)e.Node);
		}

		private void diagram_NodeClicked(object sender, NodeEventArgs e)
		{
			diagram.Selection.Clear();
			e.Node.Selected = true;
			if (e.MouseButton == MouseButton.Right)
			{
				Point pos = new Point(int.Parse(e.MousePosition.X.ToString()), Convert.ToInt32(e.MousePosition.Y.ToString()));

				//diagramView.ContextMenuStrip = contextMenuStripFrame;
				//contextMenuStripFrame.Show();
				contextMenuStripView.Items[0].Visible = false;
				contextMenuStripView.Items[1].Visible = true;
				contextMenuStripView.Items[2].Visible = true;
				contextMenuStripView.Items[3].Visible = false;

				//contextMenuStripFrame.Show(pos, ToolStripDropDownDirection.BelowRight);
				contextMenuStripView.Items[1].Tag = e.Node.Id;
				contextMenuStripView.Items[2].Tag = e.Node.Id;
			}
		}

		#endregion

		#region Link's Events

		private void DiagramLinkCreated(object sender, LinkEventArgs e)
		{
			diagram.Links.Remove(e.Link);
			EventLinkCreate(int.Parse(e.Link.Origin.Id.ToString()), int.Parse(e.Link.Destination.Id.ToString()), LinkType);
		}

		private void DiagramLinkDeleting(object sender, LinkValidationEventArgs e)
		{ //TODO: new
			if (MessageBox.Show("Удалить связь?", "Подтверждение удаления", MessageBoxButtons.YesNo) == DialogResult.Yes)
			{
				EventLinkRemove(int.Parse(e.Link.Origin.Id.ToString()), int.Parse(e.Link.Destination.Id.ToString()),
								e.Link.Tag.ToString() == LinkType.IsA.ToString()
									? LinkType.IsA
									: LinkType.SubFrame);
			}
			e.Cancel = true;
		}	

		private void DiagramLinkSelecting(object sender, LinkValidationEventArgs e)
		{

		}

		private void DiagramLinkDeselected(object sender, LinkEventArgs e)
		{
			LinkChangeStyle(e.Link, e.Link.Tag.ToString() == LinkType.IsA.ToString() ? ColorPreferences.LinkIsAColor : ColorPreferences.LinkSubFrameColor, 3);
		}


		private void diagram_LinkClicked(object sender, LinkEventArgs e)
		{
			if (e.MouseButton == MouseButton.Right)
			{
				//contextMenuStripLink.Items.Clear();
				//contextMenuStripLink.Items.Add("Удалить");
				//contextMenuStripLink.Items[0].Click += toolStripMenuItemLinkRemove_Click;
				//contextMenuStripLink.Items.Add("-");
				//foreach (var val in Enum.GetNames(typeof(CommonStructs.LinkType)))
				//{
				//    contextMenuStripLink.Items.Add(val);
				//}
				diagram.Selection.Clear();
				e.Link.Selected = true;
				//contextMenuStripLink.Show(
				//    new Point(int.Parse(e.MousePosition.X.ToString()), Convert.ToInt32(e.MousePosition.Y.ToString())),
				//    ToolStripDropDownDirection.BelowRight);

				contextMenuStripView.Items[0].Visible = false;
				contextMenuStripView.Items[1].Visible = false;
				contextMenuStripView.Items[2].Visible = false;
				contextMenuStripView.Items[3].Visible = true;

				contextMenuStripView.Items[3].Tag = new Point(Convert.ToInt32(e.Link.Origin.Id), Convert.ToInt32(e.Link.Destination.Id));
				//foreach (var c in contextMenuStripLink.Items)
				//    (c as ToolStripItem).Tag = new Point(Convert.ToInt32(e.Link.Origin.Id),Convert.ToInt32(e.Link.Destination.Id));
			}
		}

		private void diagram_LinkSelected(object sender, LinkEventArgs e)
		{
			LinkChangeStyle(e.Link, e.Link.Tag.ToString() == LinkType.IsA.ToString() ? Color.LightGray : Color.DarkBlue, 5);
		}

		void LinkChangeStyle(DiagramLink link, Color color, int width)
		{
			link.Pen = new MindFusion.Drawing.Pen(color) { Width = width };
		}
		#endregion     
      
		private ShapeNode GetNodeByID(int id)
		{
			var shapeNode = diagram.Nodes.Cast<object>().Where(c => (int)((ShapeNode)c).Id == id).Cast<ShapeNode>().FirstOrDefault();
			return shapeNode;
		}

		private DiagramLink GetLinkByEnds(int from, int to)
		{
			int f, t;
			DiagramLink linkFound = null;
			foreach (var link in diagram.Links)
			{
				f = int.Parse(((DiagramLink)link).Origin.Id.ToString());
				t = int.Parse(((DiagramLink)link).Destination.Id.ToString());
				if (f == from && t == to)
				{
					linkFound = (DiagramLink)link;
				}
			}
			return linkFound;
		}

		private DiagramLink GetLinkByStartPos(Point p)
		{
			DiagramLink linkFound = null;
			foreach (var link in diagram.Links)
			{
				if ((Convert.ToInt32(((DiagramLink)link).Origin.Id)) == p.X && (Convert.ToInt32(((DiagramLink)link).Destination.Id)) == p.Y)
				{
					linkFound = (DiagramLink)link;
					break;
				}
			}
			return linkFound;
		}		

		private void diagramView_MouseWheel(object sender, MouseEventArgs e)
		{
			if (e.Location.X > 200)
			{
				if (e.Delta > 0)
					diagramView.ScrollTo(diagramView.ScrollX, diagramView.ScrollY - 5);
				else
					diagramView.ScrollTo(diagramView.ScrollX, diagramView.ScrollY + 5);
			}
			else
			{
				if (e.Delta > 0)
					diagramView.ScrollTo(diagramView.ScrollX - 5, diagramView.ScrollY);
				else
					diagramView.ScrollTo(diagramView.ScrollX + 5, diagramView.ScrollY);
			}
		}

		public void SetZoom(float zoom)
		{
			diagramView.ZoomFactor = zoom;
		}
	}
}
